package com.atguigu.gmall.activity.service.impl;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.activity.util.CacheHelper;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.MD5;
import com.atguigu.gmall.model.activity.OrderRecode;
import com.atguigu.gmall.model.activity.SeckillGoods;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class SeckillGoodsServiceImpl implements SeckillGoodsService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    /**
     * 查询全部
     * @return
     */
    @Override
    public List<SeckillGoods> findAll() {

        List<SeckillGoods> values = redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).values();
        return values;
    }

    @Override
    public SeckillGoods getSeckillGoods(Long skuId) {
        SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).get(skuId.toString());
        return seckillGoods;
    }

    /**
     * 预下单，根据用户和商品ID实现预秒杀下单
     * @param skuId
     * @param userId
     */
    @Override
    public void seckillOrder(Long skuId, String userId) {
        //检验产品状态位， 1：可以秒杀 0：秒杀结束
        String state  = (String) CacheHelper.get(skuId.toString());

        if (null ==state || "0".equals(state)){
            //已卖完
            return;
        }
        //  判断用户是否下过订单 key = seckill:user:userId;
        //  setnx key value
        String seckillKey = RedisConst.SECKILL_USER + userId;
        //  将用户信息保存到缓存中 {哪个用户，秒杀的哪件商品}
        // 用户锁定时间 单位：秒
        //    public static final int SECKILL__TIMEOUT = 60 * 60 * 1;
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(seckillKey, skuId, RedisConst.SECKILL__TIMEOUT, TimeUnit.SECONDS);
        //  判断 用户已经下过订单！
        if (!flag){
            return;
        }
        //  减少库存 redis-list 存储的数据！
        //  key = seckill:stock:46
        String stockKey = RedisConst.SECKILL_STOCK_PREFIX + skuId;
        //  redisTemplate.opsForList().leftPush(key,seckillGoods.getSkuId());
        //获取队列中的商品，如果能够获取，则商品存在，可以下单
        String rpopSkuId = (String) redisTemplate.boundListOps(stockKey).rightPop();

        if (StringUtils.isEmpty(rpopSkuId)){
            //商品售罄，更新状态位
            redisTemplate.convertAndSend("seckillpush", skuId+":0");
            //已售罄
            return;
        }

        OrderRecode orderRecode = new OrderRecode();
        orderRecode.setNum(1);
        orderRecode.setOrderStr(MD5.encrypt(skuId+userId));
        orderRecode.setUserId(userId);
        orderRecode.setSeckillGoods(this.getSeckillGoods(skuId));

        //  orderRecode这个实体类记录了秒杀的全部信息！
        String seckillOrderKey = RedisConst.SECKILL_ORDERS; // seckill:orders
        //  使用hash 数据类型 hset key=seckill:orders  field=userId value=orderRecode;
        redisTemplate.boundHashOps(seckillOrderKey).put(userId,orderRecode);

        //  减库存！mysql - redis！
        this.updateStockCount(skuId);
    }


    /**
     * 表示更新mysql -- redis 的库存数据！
     * @param skuId
     */
    private void updateStockCount(Long skuId) {
        //  获取到存储库存剩余数！
        //  key = seckill:stock:46
        String stockKey = RedisConst.SECKILL_STOCK_PREFIX + skuId;

        Long size = redisTemplate.boundListOps(stockKey).size();
        if (size%2==0){
            SeckillGoods seckillGoods = this.getSeckillGoods(skuId);
            //修改库存
            seckillGoods.setStockCount(size.intValue());
            //更新数据库
            seckillGoodsMapper.updateById(seckillGoods);
            //更改缓存
            redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).put(seckillGoods.getSkuId().toString(),seckillGoods);
        }
    }

    /**
     * 根据商品id与用户ID查看订单信息
     * @param skuId
     * @param userId
     * @return
     */
    @Override
    public Result checkOrder(Long skuId, String userId) {
        /*
        1.  判断用户是否在缓存中存在
        2.  判断用户是否抢单成功  {再判断用户是否已经预下单}
        3.  判断用户是否下过订单
        4.  判断状态位
         */
        //  setnx key value
        String seckillKey = RedisConst.SECKILL_USER + userId;
        //  将用户信息保存到缓存中 {哪个用户，秒杀的哪件商品}
        // 用户锁定时间 单位：秒
        //    public static final int SECKILL__TIMEOUT = 60 * 60 * 1;
        Boolean flag = redisTemplate.hasKey(seckillKey);
        //  判断用户是否抢单成功  {再判断用户是否已经预下单}
        if (flag){
            //  orderRecode这个实体类记录了秒杀的全部信息！
            String seckillOrderKey = RedisConst.SECKILL_ORDERS; // seckill:orders
            //  使用hash 数据类型 hset key=seckill:orders  field=userId value=orderRecode;
            Boolean res = redisTemplate.boundHashOps(seckillOrderKey).hasKey(userId);
            if (res){
                OrderRecode orderRecode = (OrderRecode) redisTemplate.boundHashOps(seckillOrderKey).get(userId);
                return Result.build(orderRecode, ResultCodeEnum.SECKILL_SUCCESS);

            }
        }
        //  判断用户是否下过订单
        //  预下单{数据存储 hset key = seckillOrderKey field = userId  value = orderRecode;} 单纯只存缓存！
        //  真正下过订单了，在点击提交订单时的操作 ！那么需要在缓存中存储一份！同时在数据库中也有存储！
        //  缓存如何存在！ hset key = RedisConst.SECKILL_ORDERS_USERS  field =userId value = orderId;
        Boolean result = redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS_USERS).hasKey(userId);
        if (result){
            //用户已经提交过订单了
            //  hget key field
            String orderId = (String) redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS_USERS).get(userId);
            return Result.build(orderId, ResultCodeEnum.SECKILL_ORDER_SUCCESS);
        }
        //  校验状态位：
        String status = (String) CacheHelper.get(skuId.toString());
        if ("0".equals(status) || StringUtils.isEmpty(status)){
            //  已售罄！
            return Result.build(null,ResultCodeEnum.SECKILL_FAIL);
        }
        //  返回数据！
        return Result.build(null,ResultCodeEnum.SECKILL_RUN);
    }
}
